package crmdna.inventory;

import static crmdna.common.AssertUtils.ensure;
import static crmdna.common.AssertUtils.ensureNotNull;
import static crmdna.common.OfyService.ofy;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;

import com.googlecode.objectify.VoidWork;

import crmdna.client.Client;
import crmdna.group.Group;

public class InventoryItemChange {

	static void recordChange(final String client, long groupId,
			final InventoryItemChangeProp prop) {
		Client.ensureValid(client);

		Group.safeGet(client, groupId);

		ensureNotNull(prop);

		prop.ms = new Date().getTime();
		int bucketNo = getBucketNo(prop.ms);

		final String id = getEntityId(groupId, bucketNo);

		ofy(client).transact(new VoidWork() {

			@Override
			public void vrun() {
				InventoryItemChangeEntity entity = ofy(client).load()
						.type(InventoryItemChangeEntity.class).id(id).get();

				if (entity == null) {
					entity = new InventoryItemChangeEntity();
					entity.groupIdDelimiter10DayBucket = id;
				}

				entity.changes.add(prop);

				ofy(client).save().entity(entity);

				// if the entity size exceeds 1Mb gae will not allow to save.
				// an exception
				// will be thrown and dev team will be notified
			}
		});
	}

	static TreeSet<InventoryItemChangeProp> getChanges(String client,
			long groupId, long startMS, long endMS) {

		int startBucketNo = getBucketNo(startMS);
		int endBucketNo = getBucketNo(endMS);

		List<String> ids = new ArrayList<>();

		for (int bucketNo = endBucketNo; bucketNo >= startBucketNo; bucketNo--) {
			ids.add(getEntityId(groupId, bucketNo));
		}

		Map<String, InventoryItemChangeEntity> map = ofy(client).load()
				.type(InventoryItemChangeEntity.class).ids(ids);

		TreeSet<InventoryItemChangeProp> treeSet = new TreeSet<>();
		for (int i = 0; i < ids.size(); i++) {
			String id = ids.get(i);

			if (!map.containsKey(id))
				continue;

			if (i < ids.size() - 1) {
				// not the last bucket
				treeSet.addAll(map.get(id).changes);
				continue;
			}

			// last element in ids list
			for (InventoryItemChangeProp prop : map.get(id).changes) {
				if (prop.ms < startMS)
					break;

				treeSet.add(prop);
			}
		}

		return treeSet;
	}

	static int getBucketNo(long ms) {
		ensure(ms > 0, "Time in ms [" + ms + "] is negative");

		int bucketNo = (int) ms / 86400 / 10;

		return bucketNo;
	}

	static String getEntityId(long groupId, int bucketNo) {
		return groupId + "||" + bucketNo;
	}

	static TreeSet<String> getEntityIds(long groupId, long startMS, long endMS) {
		ensure(endMS >= startMS);

		TreeSet<String> ids = new TreeSet<>();

		int startBucketNo = getBucketNo(startMS);
		int endBucketNo = getBucketNo(endMS);

		for (int bucketNo = startBucketNo; bucketNo <= endBucketNo; bucketNo++) {
			ids.add(getEntityId(groupId, bucketNo));
		}

		return ids;
	}
}
